﻿namespace VIT.BusinessLogicLayer
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity.Infrastructure;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;

    using VIT.DataAccessLayer;
    using VIT.DataSource;
    using VIT.DataTransferObjects.Presentation;
    using VIT.Entity;
    using VIT.Library;
    using VIT.Library.Web;
    using VIT.Library.Web.IIS;
    using VIT.DataTransferObjects;
    using VIT.Library.FileHelpers;

    public class CompanyBLL : BLLBase
    {
        private readonly ICompanyDAL _companyDAL;
        private readonly ICompanyLanguageDAL _companyLanguageDAL;
        private readonly ICompanyDomainDAL _companyDomainDAL;
        private readonly IModuleConfigDAL _moduleConfigDAL;
        private readonly ILanguageDAL _languageDAL;
        private readonly IWebConfigDAL _webconfigDAL;

        private readonly ICompanyTemplateDAL _companyTemplateDAL;

        private readonly IUserAdminDAL _userAdminDAL;

        private readonly IItemDAL _itemDAL;
        private readonly IItemImageDAL _itemImageDAL;
        private readonly ITemplateConfigDAL _templateConfigDAL;
        private readonly IProductTypeDAL _productTypeDAL;
        private readonly ITypeArticleDAL _articleTypeDAL;

        private readonly string _imagePath;
        private readonly string _domainStore;
        private readonly int _alertDay;

        #region Constructor

        public CompanyBLL()
            : this(null)
        {
        }

        public CompanyBLL(string connectionString)
            : base(connectionString)
        {
            this._companyDAL = new CompanyDAL(DatabaseFactory);
            this._companyLanguageDAL = new CompanyLanguageDAL(DatabaseFactory);
            this._companyDomainDAL = new CompanyDomainDAL(DatabaseFactory);
            this._moduleConfigDAL = new ModuleConfigDAL(DatabaseFactory);
            this._userAdminDAL = new UserAdminDAL(DatabaseFactory);
            this._itemDAL = new ItemDAL(DatabaseFactory);
            this._itemImageDAL = new ItemImageDAL(DatabaseFactory);
            this._productTypeDAL = new ProductTypeDAL(DatabaseFactory);
            this._articleTypeDAL = new TypeArticleDAL(DatabaseFactory);
            this._templateConfigDAL = new TemplateConfigDAL(DatabaseFactory);
            this._webconfigDAL = new WebConfigDAL(DatabaseFactory);
            this._languageDAL = new LanguageDAL(DatabaseFactory);
            this._companyTemplateDAL = new CompanyTemplateDAL(DatabaseFactory);

            this._imagePath = SettingsManager.AppSettings.ImageCompanyPath;
            this._domainStore = SettingsManager.AppSettings.DomainStore;
            this._alertDay = SettingsManager.AppSettings.AlertDay;
        }

        #endregion

        #region Function

        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1303:ConstFieldNamesMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")]
        public void RegisWeb(string language, 
            string template,
            string[] domains, 
            string companyName, 
            string email, 
            string phone, 
            string fullName,
            string userName, 
            string password)
        {
            // validate domain
            var checkDomain = this._companyDomainDAL.GetAll()
                .Where(e => domains.Contains(e.Domain.ToLower()))
                .Select(e => e.Domain).FirstOrDefault();
            if (checkDomain != null) throw new BusinessLogicLayerException("Tên miền '" + checkDomain + "' đã có người sử dụng");

            foreach (var domain in domains)
            {
                try
                {
                    if (!domain.Contains("."))
                        throw new BusinessLogicLayerException("Tên miền '" + domain + "' không đúng định dạng, phải chứa ký tự '.'");
                   
                    var d = domain.Split('.');
                    if (string.IsNullOrEmpty(d[0]))
                        throw new BusinessLogicLayerException("Tên miền '" + domain + "' không đúng định dạng, tên miền không được rỗng");

                    if (string.IsNullOrEmpty(d[1]))
                        throw new BusinessLogicLayerException("Tên miền '" + domain + "' không đúng định dạng, đuôi tên miền không được rỗng");
                }
                catch
                {
                    throw new BusinessLogicLayerException("Tên miền '" + domain + "' không hợp lệ, không đúng định dạng");
                }
            }

            // validate email
            var regex = new System.Text.RegularExpressions.Regex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
            var result = regex.IsMatch(email);
            if (result == false)
            {
                throw new BusinessLogicLayerException("Địa chỉ email không hợp lệ.");
            }

            var checkUser = this._userAdminDAL.GetAll().Any(e => e.UserInfo.UserAccount.UserName.ToLower() == userName.ToLower());
            if (checkUser) throw new BusinessLogicLayerException("Tài khoản '" + userName + "' đã có người sử dụng");

            const string userAdmin = "administrator";

            #region B0: Get du lieu can thiet de tao du lieu mac dinh
            var items = this._itemDAL.GetAllIncludeAll()
            .Where(e => e.CompanyId == 2 && e.IsPublished && !e.HasDeleted)
            .ToList();

            // tat ca danh muc cua company default 
            var listAllCategories = items.Where(e => e.Category != null).ToList();
            var listCategoryId = new List<int>();

            // lay cac danh muc duoc su dung cho template
            listCategoryId.AddRange(this.GetCategoriesUsing(items, template));

            // bo sung danh muc con cho cac danh muc su dung cho template
            var childIds = new List<int>();
            foreach (var id in listCategoryId)
            {
                // new check roi thi bo qua
                if (!childIds.Contains(id))
                {
                    var newIds = this.GetAllChildCategoryIds(listAllCategories, id);
                    childIds.AddRange(newIds);
                }
            }
            listCategoryId.AddRange(childIds);

            // lay danh muc manufacturer 
            var ManufacturerIds = items.Where(e => e.Product != null && listCategoryId.Contains(e.Product.CategoryId))
                            .Where(e => e.Product.ProductCar != null && e.Product.ProductCar.ManufacturerId !=null)
                            .Select(e => e.Product.ProductCar.ManufacturerId)
                            .ToList();
            var manuCats = items.Where(e => e.ProductManufacturer != null && ManufacturerIds.Contains(e.Id))
                            .Select(e => e.ProductManufacturer.CategoryId)
                            .ToList();
            listCategoryId.AddRange(manuCats);
            

            // loai bo danh muc khong su dung
            listCategoryId = listCategoryId.Distinct().ToList();
            var categories = this.CreateCategoriesUsing(listAllCategories, listCategoryId);

            // get cac thong tin can thiet cho B1, B2, B3
            var companyInfo = this._companyDAL.GetAll().Where(e => e.Id == 2).FirstOrDefault();
            var companyInfos = this._companyLanguageDAL.GetAll().Where(e => e.CompanyId == 2).ToList();

            var roleDefailt = this._userAdminDAL.GetAll()
                                        .Where(e => e.UserInfo.UserAccount.UserName == "default_1" && e.UserInfo.CompanyId == 2)
                                        .Select(e => e.Roles)
                                        .FirstOrDefault();

            var tempWebconfig = this._webconfigDAL.Get(e => e.CompanyId == 2);
            var tempTemplateConfig = this._templateConfigDAL.Get(e => e.CompanyId == 2 && e.TemplateName == template);
            var languages = this._languageDAL.GetAll().Where(e => e.CompanyId == 2).Select(e => new { e.Id, e.IsDefault }).ToList();

            #endregion

            // mo transaction bat dau xu ly
            var conn = ((IObjectContextAdapter)DatabaseFactory.Get()).ObjectContext.Connection;
            conn.Open();
            var trans = conn.BeginTransaction();
            try
            {
                #region B1: Tao company: table Company, CompanyLanguage, CompanyDomain, CompanyTemplate
                var company = new Company();
                company.Email = email;
                company.Phone = phone;
                company.Fax = phone;
                company.IsPublished = true;
                company.IsDeleted = false;
                company.CreateByUser = userAdmin;
                company.CreateDate = DateTime.Now;
                if (companyInfo != null) company.Image = companyInfo.Image;

                foreach (var info in companyInfos)
                {
                    var companyLanguage = new CompanyLanguage();
                    companyLanguage.FullName = companyName;
                    companyLanguage.DisplayName = companyName;

                    companyLanguage.LanguageId = info.LanguageId;
                    companyLanguage.Slogan = info.Slogan;
                    companyLanguage.Address = info.Address;
                    companyLanguage.AboutUs = info.AboutUs;
                    company.CompanyLanguages.Add(companyLanguage);
                }

                foreach (var domain in domains)
                {
                    var companyDomain = new CompanyDomain { Domain = domain.Trim() };
                    company.CompanyDomains.Add(companyDomain);
                }

                var companyTemplate = new CompanyTemplate { TemplateName = template };
                company.CompanyTemplates.Add(companyTemplate);
                #endregion

                #region B2: Tao user: table UserInfo, UserAccount, UserAdmin (chu y roles default)
                var account = new UserAccount();
                account.UserName = userName;
                account.Password = password.EnCodeMD5();

                // copy role from user Default_1
                var admin = new UserAdmin();
                admin.CreateByUser = userAdmin;
                admin.Roles = roleDefailt;

                var user = new UserInfo();
                user.FullName = fullName;
                user.Email = email;
                user.UserAccount = account;
                user.UserAdmin = admin;
                user.CreateDate = DateTime.Now;
                company.UserInfoes.Add(user);

                this._companyDAL.Add(company);
                this.DatabaseFactory.Get().SaveChanges();
                #endregion

                #region B3: cau hinh cho web: table WebConfig, language
                var webConfig = new WebConfig();
                webConfig.CreateDate = DateTime.Now;
                webConfig.PreTemplate = template;
                webConfig.AdminTemplate = "Admin";
                webConfig.Keys = string.Empty;

                webConfig.UseTrash = false;
                webConfig.Redirect = 1;

                if (tempWebconfig != null)
                {
                    webConfig.Background = tempWebconfig.Background;
                    webConfig.BackgroundPosition = tempWebconfig.BackgroundPosition;
                    webConfig.BackgroundRepeat = tempWebconfig.BackgroundRepeat;

                    webConfig.GoogleAnalytics = tempWebconfig.GoogleAnalytics;
                    webConfig.LinkGoPublic = "http://" + domains.FirstOrDefault();
                    webConfig.WebIcon = tempWebconfig.WebIcon;
                }

                if (tempTemplateConfig != null)
                {
                    webConfig.Background = tempTemplateConfig.Background;
                    webConfig.BackgroundPosition = tempTemplateConfig.BackgroundPosition;
                    webConfig.BackgroundRepeat = tempTemplateConfig.BackgroundRepeat;
                }
                company.WebConfigs.Add(webConfig);

                foreach (var l in languages)
                {
                    var obj = new Language();
                    obj.Id = l.Id;
                    obj.IsDefault = l.IsDefault;
                    obj.CompanyId = company.Id;
                    obj.CreateByUser = userName;
                    obj.CreateDate = DateTime.Now;
                    this._languageDAL.Add(obj);
                }
                #endregion

                // B4: Tao du lieu mau: copy du lieu theo template cua company 1
                var mapItems = this.CreateDefaultDataForNewCompany(items, categories, company.Id, userName);

                // B5: Tao module defaule: copy du lieu theo template cua company 1
                this.CreateModuleDefaultForNewCompany(items, template, company.Id, userName, mapItems);

                this.SaveChanges();
                trans.Commit();

                // B6: copy file tu company 2 sang company moi
                var source = string.Format(SettingsManager.AppSettings.FolderUpload, "2");
                var dest = string.Format(SettingsManager.AppSettings.FolderUpload, company.Id);
                this.CopyFolder(source, dest);

                // Step 7: Add domain binding for website                
                Binding.Add(domains.ToList());
            }
            catch (BusinessLogicLayerException)
            {
                trans.Rollback();
                throw;
            }
            catch (Exception ex)
            {
                trans.Rollback();
                throw new BusinessLogicLayerException(string.Format("Đăng ký không thành công. Vì có quá nhiều người đang đăng ký, vui lòng quay lại sau hoặc liên hệ bộ phận kỹ thuật nhờ giúp đỡ: {0}.", ex.Message));
            }
            finally
            {
                conn.Close();
            }
        }

        public CompanyInfoDto GetDetalsShowById(int companyId, string language)
        {
            var dtoQuery = this._companyDAL.GetAll()
                .Where(c => c.Id == companyId && c.IsDeleted == false)
                .Select(o => new CompanyInfoDto
                {
                    ImageName = o.Image,
                    Email = o.Email,
                    Phone = o.Phone,
                    Fax = o.Fax
                });

            var dto = dtoQuery.FirstOrDefault();

            if (dto == null) return new CompanyInfoDto();

            var dtoLanguage = this._companyLanguageDAL.Get(cl => cl.CompanyId == companyId && cl.LanguageId.ToLower() == language.ToLower());
            if (dtoLanguage != null)
            {
                dto.DisplayName = dtoLanguage.DisplayName;
                dto.FullName = dtoLanguage.FullName;
                dto.Slogan = dtoLanguage.Slogan;
                dto.AboutUs = dtoLanguage.AboutUs;
                dto.Address = dtoLanguage.Address;
            }

            // update path image
            var imagePath = string.Format(this._imagePath, companyId);
            dto.ImagePath = this.VirtualPath(imagePath, dto.ImageName);

            return dto;
        }

        public DataSimpleDto GetDataSingle(int companyId, string languageId)
        {
            var query = this._companyLanguageDAL.GetAll()
                .Where(e => e.Company.Id == companyId && e.LanguageId == languageId);

            var selected = query.Select(a => new DataSimpleDto
            {
                Title = a.DisplayName,
                Description = a.AboutUs,
                ImagePath = a.Company.Image
            });

            var data = selected.FirstOrDefault();

            if (data != null && !string.IsNullOrEmpty(data.ImagePath))
            {
                // update path image
                var imagePath = string.Format(this._imagePath, companyId);
                data.ImagePath = this.VirtualPath(imagePath, data.ImagePath);
            }

            return data;
        }

        // support CRM
        public IList<CustomerStatusDto> GetAllWebsite(int freeDay)
        {
            var dtoQuery = this._webconfigDAL.GetAll()
                .Select(o => new CustomerStatusDto
                    {
                        Id = o.Company.CompanyDomains.Select(e => e.Domain).FirstOrDefault(),
                        CreateDate = o.CreateDate,
                        RegisDate = o.RegisDate,
                        ExperDate = o.ExperDate
                    });

            var dto = dtoQuery.ToList();

            foreach (var com in dto)
            {
                // neu dang dung thu
                if (com.RegisDate == null)
                {
                    var dayUses = (DateTime.Now - com.CreateDate).Days;
                    if (dayUses > freeDay) com.StatusId = 3; // het han dung thu
                    else if (freeDay - dayUses > this._alertDay) com.StatusId = 1; // dang dung thu
                    else com.StatusId = 2; // sap het han dung thu
                }
                else // da dang ky
                {
                    if (DateTime.Now > com.ExperDate) com.StatusId = 6; // het han su dung
                    else if ((com.ExperDate.Value - DateTime.Now).Days > this._alertDay) com.StatusId = 4; // dang dung thu
                    else com.StatusId = 5; // sap het han su dung
                }
            }

            return dto;
        }

        public string GetAddressById(int companyId, string language)
        {
            var address = this._companyLanguageDAL.GetAll()
                .Where(cl => cl.CompanyId == companyId && cl.LanguageId == language)
                .Select(e => e.Address)
                .FirstOrDefault();

            return address;
        }

        public string GetEmailById(int companyId)
        {
            var address = this._companyDAL.GetAll()
                .Where(cl => cl.Id == companyId)
                .Select(e => e.Email)
                .FirstOrDefault();

            return address;
        }
        #region domain
        public CustomerDetailDto GetCompanyDetailByDomain(string domain)
        {
            var companyId = this._companyDomainDAL.GetAll()
                .Where(e => e.Domain.ToLower() == domain.ToLower())
                .Select(e => e.CompanyId).FirstOrDefault();

            if(companyId != null)
            {
                var dto = this._webconfigDAL.GetAll()
                    .Where(e => e.CompanyId == companyId)
                    .Select(e => new CustomerDetailDto
                    {
                       Id = domain,
                       Key = e.Keys,
                       CreateDate = e.CreateDate,
                       RegisDate = e.RegisDate,
                       ExperDate = e.ExperDate,

                       CompanyPhone = e.Company.Phone,
                       CompanyEmail = e.Company.Email,
                       CompanyName= e.Company.CompanyLanguages.FirstOrDefault().FullName,
                       CompanyAddress = e.Company.CompanyLanguages.FirstOrDefault().Address,

                       UserName = e.Company.UserInfoes.FirstOrDefault().FullName,
                       UserEmail = e.Company.UserInfoes.FirstOrDefault().Email,
                       UserPhone = e.Company.UserInfoes.FirstOrDefault().Mobile,
                       UserAddress = e.Company.UserInfoes.FirstOrDefault().Address
                    })
                    .FirstOrDefault();

                if (dto != null)
                {
                    var templates = this._companyTemplateDAL.GetAll()
                        .Where(e => e.CompanyId == companyId)
                        .Select(e => e.TemplateName)
                        .ToList();
                    dto.Templates = string.Join(", ", templates);

                    var domains = this._companyDomainDAL.GetAll()
                        .Where(e => e.CompanyId == companyId)
                        .Select(e => e.Domain.ToLower())
                        .ToList();
                    dto.Domains = domains;

                    return dto;
                }
            }
            return new CustomerDetailDto();
        }

        public IList<string> GetDomainsByDomain(string domain)
        {
            var companyId = this._companyDomainDAL.GetAll()
                .Where(e => e.Domain.ToLower() == domain.ToLower())
                .Select(e => e.CompanyId).FirstOrDefault();

            if (companyId != null)
            {
                var domains = this._companyDomainDAL.GetAll()
                    .Where(e => e.CompanyId == companyId)
                    .Select(e => e.Domain)
                    .ToList();

                return domains;
            }

            return new List<string>();
        }

        public IList<string> GetTemplatesByDomain(string domain)
        {
            var companyId = this._companyDomainDAL.GetAll()
                .Where(e => e.Domain.ToLower() == domain.ToLower())
                .Select(e => e.CompanyId).FirstOrDefault();

            if (companyId != null)
            {
                var templates = this._companyTemplateDAL.GetAll()
                    .Where(e => e.CompanyId == companyId)
                    .Select(e => e.TemplateName)
                    .ToList();

                return templates;
            }

            return new List<string>();
        }

        public int GetCompanyIdByDomain(string domain)
        {
            var companyId = this._companyDomainDAL.GetAll()
                .Where(e => e.Domain.ToLower() == domain.ToLower())
                .Select(e => e.CompanyId).FirstOrDefault();

            return companyId;
        }

        public void AddDomainCompany(int companyId, string domain)
        {
            try
            {
                // validate domain
                // check using by other company
                var checkDomain = this._companyDomainDAL.GetAll()
                    .Where(e => e.CompanyId != companyId && domain.ToLower() == e.Domain.ToLower())
                    .Select(e => e.Domain)
                    .FirstOrDefault();
                if (checkDomain != null) throw new System.Configuration.Provider.ProviderException("Domain '" + domain + "' đã có người sử dụng");

                var obj = this._companyDomainDAL.Get(o => o.Domain == domain && o.CompanyId == companyId);
                if (obj == null)
                {
                    obj = new CompanyDomain();
                    obj.CompanyId = companyId;
                    obj.Domain = domain;
                    this._companyDomainDAL.Add(obj);
                }

                this.SaveChanges();

                try
                {
                    var th = new System.Threading.Thread(delegate()
                    {
                        var url = string.Format("http://{0}/{1}/AddBinding", SettingsManager.AppSettings.DomainPublic, SettingsManager.Constants.JsonPage);
                        var arrdomain = new[] { domain };
                        var tk = string.Join("|", arrdomain).EnCode(arrdomain.Length);

                        var json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(new { domains = arrdomain, token = tk });

                        (new URL()).PostJson(url, json);
                    });
                    th.Start();
                }
                catch (Exception ex)
                {
                }
            }
            catch (Exception ex)
            {
                throw new BusinessLogicLayerException(ex.Message);
            }
        }

        public void RemoveDomainCompany(int companyId, string domain)
        {
            try
            {
                // validate domain
                var obj = this._companyDomainDAL.Get(o => o.Domain == domain && o.CompanyId == companyId);
                if (obj != null) this._companyDomainDAL.Delete(obj);
                this.SaveChanges();

                try
                {
                    var th = new System.Threading.Thread(delegate()
                    {
                        var url = string.Format("http://{0}/{1}/RemoveBinding", SettingsManager.AppSettings.DomainPublic, SettingsManager.Constants.JsonPage);
                        var arrdomain = new[] { domain };
                        var tk = string.Join("|", arrdomain).EnCode(arrdomain.Length);

                        var json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(new { domains = arrdomain, token = tk });

                        (new URL()).PostJson(url, json);
                    });
                    th.Start();
                }
                catch (Exception ex)
                {
                }
            }
            catch (Exception ex)
            {
                throw new BusinessLogicLayerException(ex.Message);
            }
        }
        #endregion

        #region template

        #endregion
        #endregion

        #region private method

        /// <summary>
        /// Ham tao du lieu mac dinh cho web moi dang ky
        /// </summary>
        /// <param name="items">
        /// Danh sach tat ca du lieu mac dinh can copy qua
        /// </param>
        /// <param name="newCompanyId">
        /// Ma cong ty moi vua khoi tao
        /// </param>
        /// <param name="userName">
        /// Ma nguoi dang ky
        /// </param>
        /// <returns>
        /// Tra ve bang Map giu Id cu va Id moi vua copy qua
        /// </returns>
        private IDictionary<int, int> CreateDefaultDataForNewCompany(IList<Item> items, IList<Item> categories, int newCompanyId, string userName)
        {
            // danh muc duoc su dung trong template
            var listCategoryId = categories.Select(e => e.Id).ToList();

            // copy tat ca du lieu cua company 2 sang company moi
            // su dung cac bien map de luu Id cu va moi su dung lai cho module
            var mapItems = new Dictionary<int, int>();

            #region du lieu mac dinh cua danh muc
            this.CopyCategories(mapItems, categories, null, userName, newCompanyId);

            #endregion
            
            #region du lieu mac dinh cua bai viet
            var articles = items.Where(e => e.Article != null && listCategoryId.Contains(e.Article.CategoryId)).ToList();

            // type trong table prosuct cach nhau dau ","
            var usingArticleTypes = string.Join(",", articles.Select(e => e.Article.Type))
                                .Split(',')
                                .Where(e => !string.IsNullOrEmpty(e))
                                .Distinct().ToList();
            var articleTypes = _articleTypeDAL.GetAll()
                .Where(e => e.CompanyId == 2 && usingArticleTypes.Contains(e.Id))
                .Select(e => new { e.Id, e.Description })
                .ToList();
            foreach (var e in articleTypes)
            {
                var articleType = new TypeArticle();
                articleType.CompanyId = newCompanyId;
                articleType.Id = e.Id;
                articleType.Description = e.Description;
                this._articleTypeDAL.Add(articleType);
            }

            foreach (var item in articles)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new Article();
                obj.Image = item.Article.Image;
                obj.CategoryId = mapItems[item.Article.CategoryId];
                obj.Type = item.Article.Type;
                obj.Tag = item.Article.Tag;
                obj.HasComment = item.Article.HasComment;
                obj.DisplayDate = DateTime.Now;
                newItem.Article = obj;

                foreach (var cl in item.Article.ArticleLanguages)
                {
                    var lang = new ArticleLanguage();
                    lang.Contents = cl.Contents;
                    lang.Title = cl.Title;
                    lang.Brief = cl.Brief;
                    lang.Contents = cl.Contents;
                    lang.LanguageId = cl.LanguageId;
                    newItem.Article.ArticleLanguages.Add(lang);
                }

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
            #endregion

            #region du lieu mac dinh cua link-logo
            var links = items.Where(e => e.Link != null && listCategoryId.Contains(e.Link.CategoryId ?? 0)).ToList();
            foreach (var item in links)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new Link();
                obj.ImageAvatar = item.Link.ImageAvatar;
                obj.CategoryId = mapItems[item.Link.CategoryId ?? 0];
                obj.Target = item.Link.Target;
                obj.URL = item.Link.URL;
                newItem.Link = obj;

                foreach (var cl in item.Link.LinkLanguages)
                {
                    var lang = new LinkLanguage();
                    lang.Description = cl.Description;
                    lang.Title = cl.Title;
                    lang.LanguageId = cl.LanguageId;
                    newItem.Link.LinkLanguages.Add(lang);
                }

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
            #endregion
            
            #region du lieu mac dinh cua color-style
            var colors = items.Where(e => e.Color != null).ToList();
            foreach (var item in colors)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new Color();
                obj.ImageName = item.Color.ImageName;
                obj.Name = item.Color.Name;
                obj.Description = item.Color.Description;
                obj.Value = item.Color.Value;
                newItem.Color = obj;

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }

            var styles = items.Where(e => e.Style != null).ToList();
            foreach (var item in styles)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new Style();
                obj.ImageName = item.Style.ImageName;
                obj.Name = item.Style.Name;
                obj.Description = item.Style.Description;
                newItem.Style = obj;

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
            #endregion

            #region du lieu mac dinh cua support-online
            var supports = items.Where(e => e.SupportOnline != null && listCategoryId.Contains(e.SupportOnline.CategoryId)).ToList();
            foreach (var item in supports)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new SupportOnline();
                obj.FullName = item.SupportOnline.FullName;
                obj.CategoryId = mapItems[item.SupportOnline.CategoryId];
                obj.NickName = item.SupportOnline.NickName;
                obj.Phone = item.SupportOnline.Phone;
                obj.TypeId = item.SupportOnline.TypeId;
                newItem.SupportOnline = obj;

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
            #endregion

            #region du lieu mac dinh cua province
            var provinces = items.Where(e => e.Province != null).ToList();
            foreach (var item in provinces)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new Province();
                obj.Code = item.Province.Code;
                obj.Name = item.Province.Name;
                newItem.Province = obj;

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
            #endregion

            #region du lieu mac dinh cua file
            var files = items.Where(e => e.File != null && listCategoryId.Contains(e.File.CategoryId ?? 0)).ToList();
            foreach (var item in files)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new VIT.Entity.File();
                obj.CategoryId = mapItems[item.File.CategoryId ?? 0];
                obj.FileName = item.File.FileName;
                obj.FileUrl = item.File.FileUrl;
                obj.Size = item.File.Size;
                obj.Tag = item.File.Tag;
                newItem.File = obj;

                if (item.File.FileAudio != null)
                {
                    var audio = new FileAudio();
                    audio.Author = item.File.FileAudio.Author;
                    audio.BitRate = item.File.FileAudio.BitRate;
                    audio.Embed = item.File.FileAudio.Embed;
                    audio.Lyrics = item.File.FileAudio.Lyrics;
                    audio.Time = item.File.FileAudio.Time;
                    newItem.File.FileAudio = audio;
                }

                if (item.File.FileDocument != null)
                {
                    var doc = new FileDocument();
                    doc.Author = item.File.FileDocument.Author;
                    doc.Extension = item.File.FileDocument.Extension;
                    doc.Image = item.File.FileDocument.Image;
                    doc.Pages = item.File.FileDocument.Pages;
                    newItem.File.FileDocument = doc;
                }

                if (item.File.FileVideo != null)
                {
                    var video = new FileVideo();
                    video.Capture = item.File.FileVideo.Capture;
                    video.Director = item.File.FileVideo.Director;
                    video.Editor = item.File.FileVideo.Editor;
                    video.Embed = item.File.FileVideo.Embed;
                    video.Time = item.File.FileVideo.Time;
                    video.Writer = item.File.FileVideo.Writer;
                    newItem.File.FileVideo = video;
                }

                foreach (var cl in item.File.FileLanguages)
                {
                    var lang = new FileLanguage();
                    lang.Description = cl.Description;
                    lang.Title = cl.Title;
                    lang.LanguageId = cl.LanguageId;
                    newItem.File.FileLanguages.Add(lang);
                }

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
            #endregion

            #region du lieu mac dinh cua product
            var products = items.Where(e => e.Product != null && listCategoryId.Contains(e.Product.CategoryId)).ToList();

            // type trong table prosuct cach nhau dau ","
            var usingProductTypes = string.Join(",", products.Select(e => e.Product.Type))
                                .Split(',')
                                .Where(e => !string.IsNullOrEmpty(e))
                                .Distinct().ToList();
            var productTypes = _productTypeDAL.GetAll()
                .Where(e => e.CompanyId == 2 && usingProductTypes.Contains(e.Id))
                .Select(e => new { e.Id, e.Description })
                .ToList();
            foreach (var e in productTypes)
            {
                var productType = new ProductType();
                productType.CompanyId = newCompanyId;
                productType.Id = e.Id;
                productType.Description = e.Description;
                this._productTypeDAL.Add(productType);
            }

            // hien tao manufacturer chi dung cho table productcar
            var usingManufacturers = products.Where(e => e.Product.ProductCar != null && e.Product.ProductCar.ManufacturerId != null)
                                    .Select(e => e.Product.ProductCar.ManufacturerId);
            var menufacturers = items.Where(e => e.ProductManufacturer != null && usingManufacturers.Contains(e.Id)).ToList();
            foreach (var item in menufacturers)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new ProductManufacturer();
                obj.CategoryId = mapItems[item.ProductManufacturer.CategoryId];
                obj.Image = item.ProductManufacturer.Image;
                obj.Name = item.ProductManufacturer.Name;
                newItem.ProductManufacturer = obj;

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }

            // table model hien tai chi su dung cho manufacturer
            var models = items.Where(e => e.ProductModel != null && usingManufacturers.Contains(e.ProductModel.ManufacturerId)).ToList();
            foreach (var item in models)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new ProductModel();
                obj.ManufacturerId = mapItems[item.ProductModel.ManufacturerId ?? 0];
                obj.Name = item.ProductModel.Name;
                newItem.ProductModel = obj;

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }
                        
            foreach (var item in products)
            {
                var newItem = new Item();
                newItem.CreateByUser = userName;
                newItem.CreateDate = DateTime.Now;
                newItem.CompanyId = newCompanyId;
                newItem.IsPublished = true;
                newItem.HasDeleted = false;
                newItem.Orders = item.Orders;

                var obj = new Product();
                obj.CategoryId = mapItems[item.Product.CategoryId];
                obj.Color = item.Product.Color;
                obj.Image = item.Product.Image;
                obj.ImageSrc = item.Product.ImageSrc;
                obj.Price = item.Product.Price;
                obj.Sale = item.Product.Sale;
                obj.Style = item.Product.Style;
                obj.Tag = item.Product.Tag;
                obj.Type = item.Product.Type;
                newItem.Product = obj;

                if (item.Product.ProductCar != null)
                {
                    var car = new ProductCar();
                    car.Amenities = item.Product.ProductCar.Amenities;
                    car.ContactAddress = item.Product.ProductCar.ContactAddress;
                    car.ContactMan = item.Product.ProductCar.ContactMan;
                    car.ContactTel = item.Product.ProductCar.ContactTel;
                    car.DrivingStyle = item.Product.ProductCar.DrivingStyle;
                    car.EstimateKm = item.Product.ProductCar.EstimateKm;
                    car.Fuel = item.Product.ProductCar.Fuel;
                    car.Gear = item.Product.ProductCar.Gear;
                    car.InProduction = item.Product.ProductCar.InProduction;
                    car.Made = item.Product.ProductCar.Made;
                    car.ManufacturerId = mapItems[item.Product.ProductCar.ManufacturerId ?? 0];
                    car.ModelId = mapItems[item.Product.ProductCar.ModelId ?? 0];
                    car.PriceType = item.Product.ProductCar.PriceType;
                    car.Producer = item.Product.ProductCar.Producer;
                    car.ProvinceId = mapItems[item.Product.ProductCar.ProvinceId ?? 0]; 
                    car.RemainNew = item.Product.ProductCar.RemainNew;
                    car.SafetyEquipment = item.Product.ProductCar.SafetyEquipment;
                    car.Status = item.Product.ProductCar.Status;
                    car.Type = item.Product.ProductCar.Type;
                    newItem.Product.ProductCar = car;
                }

                foreach (var cl in item.Product.ProductLanguages)
                {
                    var lang = new ProductLanguage();
                    lang.Description = cl.Description;
                    lang.Title = cl.Title;
                    lang.Brief = cl.Brief;
                    lang.LanguageId = cl.LanguageId;
                    newItem.Product.ProductLanguages.Add(lang);
                }

                this._itemDAL.Add(newItem);
                this.DatabaseFactory.Get().SaveChanges();
                mapItems[item.Id] = newItem.Id;
            }     

            #endregion

            #region du lieu mac dinh cua image
            var images = this._itemImageDAL.GetAll()
                .Where(e => e.Item.CompanyId == 2 && e.Item.IsPublished && !e.Item.HasDeleted)
                .Where(e => mapItems.Keys.Contains(e.Item.Id))
                .ToList();
            foreach (var image in images)
            {
                var img = new ItemImage();
                img.ItemId = mapItems[image.ItemId ?? 0];
                img.Description = image.Description;
                img.ImageName = image.ImageName;
                this._itemImageDAL.Add(img);
            }
            #endregion

            return mapItems;
        }

        /// <summary>
        /// Ham tao module mau cho web moi dang ky.
        /// </summary>
        /// <param name="items">
        /// Danh sach du lieu mau
        /// </param>
        /// <param name="templateName">
        /// The template name.
        /// </param>
        /// <param name="companyId">
        /// Ma cong ty dang ky
        /// </param>
        /// <param name="user">
        /// Nguoi dang ky
        /// </param>
        /// <param name="mapIds">
        /// Tra ve bang Map giu Id cu va Id moi vua copy qua
        /// </param>
        private void CreateModuleDefaultForNewCompany(IList<Item> items, string templateName, int companyId, string user, IDictionary<int, int> mapIds)
        {
            var modules = items.Where(e => e.ModuleConfig != null && e.ModuleConfig.TemplateName.ToLower() == templateName.ToLower()).ToList();

            foreach (var item in modules)
            {
                var languageList = item.ModuleConfig.ModuleConfigLanguages;
                var paramList = item.ModuleConfig.ModuleConfigParams;

                var obj = new ModuleConfig();
                obj.ComponentName = item.ModuleConfig.ComponentName;
                obj.ComponentPosition = item.ModuleConfig.ComponentPosition;
                obj.Name = item.ModuleConfig.Name;
                obj.SkinName = item.ModuleConfig.SkinName;
                obj.TemplateName = item.ModuleConfig.TemplateName;
                obj.TemplatePosition = item.ModuleConfig.TemplatePosition;
                obj.ViewName = item.ModuleConfig.ViewName;

                obj.Item = new Item();
                obj.Item.CreateByUser = user;
                obj.Item.CreateDate = DateTime.Now;
                obj.Item.HasDeleted = false;
                obj.Item.IsPublished = true;
                obj.Item.Orders = item.Orders;

                // neu la template admin thi ko luu company
                if (item.ModuleConfig.TemplateName.Trim().ToLower() != "admin") obj.Item.CompanyId = companyId;

                if (languageList != null)
                {
                    foreach (var language in languageList)
                    {
                        obj.ModuleConfigLanguages.Add(new ModuleConfigLanguage
                        {
                            Title = language.Title,
                            LanguageId = language.LanguageId
                        });
                    }
                }

                if (paramList != null)
                {
                    foreach (var param in paramList)
                    {
                        var moduleParam = new ModuleConfigParam();
                        moduleParam.ParamName = param.ParamName;
                        if (DataSource.ParameterNameCollection.Any(e => e.Id.ToLower() == param.ParamName.ToLower()))
                        {
                            int id;
                            int.TryParse(param.Value, out id);

                            if (mapIds.ContainsKey(id)) moduleParam.Value = mapIds[id].ToString();
                            else moduleParam.Value = "0";
                        }
                        else moduleParam.Value = param.Value;

                        obj.ModuleConfigParams.Add(moduleParam);
                    }
                }

                this._moduleConfigDAL.Add(obj);
            }
        }

        /// <summary>
        /// Ham lay ra danh sach danh muc dang truoc template su dung.
        /// </summary>
        /// <param name="items">
        /// Danh sach tat ca du lieu mac dinh
        /// </param>
        /// <param name="templateName">
        /// The template name.
        /// </param>
        /// <returns>
        /// Danh sach Ma danh muc
        /// </returns>
        private IList<int> GetCategoriesUsing(IList<Item> items, string templateName)
        {
            // Dau tien lay tat ca ma danh muc , ma bai viet, ma san pham co trong param cua module
            var listParam = items.Where(e => e.ModuleConfig != null && e.ModuleConfig.ModuleConfigParams != null && e.ModuleConfig.TemplateName.ToLower() == templateName)
                                .Select(e => e.ModuleConfig.ModuleConfigParams)
                                .ToList();
            var listCategoryId = new List<int>();
            var listArticleId = new List<int>();
            var listProductId = new List<int>();
            var listManufacturerId = new List<int>();
            foreach (var param in listParam)
            {
                var categorieIds = param.Where(e => (e.ParamName == "CategoryId" || e.ParamName == "ParentCategoryId") && !string.IsNullOrEmpty(e.Value))
                                .Select(e => Convert.ToInt32(e.Value)).Distinct()
                                .ToList();
                listCategoryId.AddRange(categorieIds);

                var articleIds = param.Where(e => (e.ParamName == "ArticleId" || e.ParamName == "OrtherArticleId") && !string.IsNullOrEmpty(e.Value))
                                .Select(e => Convert.ToInt32(e.Value)).Distinct()
                                .ToList();
                listArticleId.AddRange(articleIds);

                var productIds = param.Where(e => (e.ParamName == "ProductId" || e.ParamName == "OrtherProductId") && !string.IsNullOrEmpty(e.Value))
                                .Select(e => Convert.ToInt32(e.Value)).Distinct()
                                .ToList();
                listProductId.AddRange(productIds);

                var manufacturerIds = param.Where(e => e.ParamName == "ManufacturerId" && !string.IsNullOrEmpty(e.Value))
                                .Select(e => Convert.ToInt32(e.Value)).Distinct()
                                .ToList();
                listManufacturerId.AddRange(manufacturerIds);
            }

            listArticleId = listArticleId.Distinct().ToList();
            listProductId = listProductId.Distinct().ToList();
            listManufacturerId = listManufacturerId.Distinct().ToList();

            // tiep theo lay ma danh muc cua bai viet va san pham co trong module
            var artCatIds = items.Where(e => e.Article != null && listArticleId.Contains(e.Id))
                                .Select(e => e.Article.CategoryId)
                                .Distinct()
                                .ToList();
            listCategoryId.AddRange(artCatIds);

            var proCatIds = items.Where(e => e.Product != null && listProductId.Contains(e.Id))
                                .Select(e => e.Product.CategoryId)
                                .Distinct()
                                .ToList();
            listCategoryId.AddRange(proCatIds);

            var manuCatIds = items.Where(e => e.ProductManufacturer != null && listProductId.Contains(e.Id))
                                .Select(e => e.Product.ProductCar)
                                .Distinct()
                                .ToList();

            listCategoryId = listCategoryId.Distinct().ToList();

            // var listCategoryId = items.Where(e => e.Category != null).Select(e => e.Id).ToList();
            return listCategoryId;
        }

        /// <summary>
        /// Ham Get tat ca cac Id con cua 1 category
        /// </summary>
        /// <param name="categories">
        /// Danh sach Category goc
        /// </param>
        /// <param name="parentId">
        /// Lay con cua Danh muc nay
        /// </param>
        /// <returns>
        /// Danh sach Id con
        /// </returns>
        private IList<int> GetAllChildCategoryIds(IList<Item> categories, int? parentId)
        {
            var result = new List<int>();
            var subcats = categories.Where(o => o.Category.ParentId == parentId).ToList(); // lay tat ca con cua parentID
            if (!subcats.Any()) return result;

            foreach (var subcat in subcats)
            {
                if (subcat != null)
                {
                    var temp = this.GetAllChildCategoryIds(categories, subcat.Id);

                    result.Add(subcat.Id);
                    result.AddRange(temp);
                }
            }

            return result;
        }

        /// <summary>
        /// _ Ham loai bo nhung danh muc khong duoc su dung trong template
        /// _ Loai bo cac nhanh ko can thiet
        /// _ Loai bo nhom cha rong ko can thiet
        /// </summary>
        /// <param name="categories">
        /// Danh sach danh muc goc
        /// </param>
        /// <param name="listCategoryId">
        /// Danh sach ma danh muc dang duoc su dung
        /// </param>
        /// <returns>
        /// Danh sach danh muc moi
        /// </returns>
        private IList<Item> CreateCategoriesUsing(IList<Item> categories, IList<int> listCategoryId)
        {
            var result = new List<Item>();
            var usingCategories = categories.Where(e => listCategoryId.Contains(e.Id)).ToList();
            foreach (var obj in usingCategories)
            {
                var item = new Item();
                item.Id = obj.Id;
                item.Orders = obj.Orders;
                item.IsPublished = obj.IsPublished;
                item.HasDeleted = obj.HasDeleted;

                var category = new Category();
                category.Id = obj.Id;
                category.ParentId = obj.Category.ParentId;
                category.TypeId = obj.Category.TypeId;
                category.Image = obj.Category.Image;

                foreach (var cl in obj.Category.CategoryLanguages)
                {
                    var catLang = new CategoryLanguage();
                    catLang.Description = cl.Description;
                    catLang.Title = cl.Title;
                    catLang.LanguageId = cl.LanguageId;
                    category.CategoryLanguages.Add(catLang);
                }

                item.Category = category;
                result.Add(item);
            }

            foreach (var category in result)
            {
                // neu danh muc khong duoc su dung thi day danh muc con len ngang cap
                if (!listCategoryId.Contains(category.Category.ParentId ?? 0)) 
                    category.Category.ParentId = null;
            }

            result = result.Where(e => listCategoryId.Contains(e.Id)).ToList();
            return result;
        }

        /// <summary>
        /// Copy ra danh muc moi theo danh muc mau, ghi lai Id cu va Id moi tao ra
        /// </summary>
        /// <param name="mapIds">
        /// Mang Map Id cu va Id moi tao ra
        /// </param>
        /// <param name="categories">
        /// Danh sach danh muc cu.
        /// </param>
        /// <param name="parentId">
        /// Ma danh muc Root.
        /// </param>
        /// <param name="userName">
        /// Nguoi tao
        /// </param>
        /// <param name="companyId">
        /// Ma cong ty moi tao.
        /// </param>
        private void CopyCategories(IDictionary<int, int> mapIds, IList<Item> categories, int? parentId, string userName, int companyId)
        {
            var subcats = categories.Where(o => o.Category.ParentId == parentId).ToList();
            if (subcats.Count != 0)
            {
                foreach (var item in subcats)
                {
                    var newItem = new Item();
                    newItem.CreateByUser = userName;
                    newItem.CreateDate = DateTime.Now;
                    newItem.CompanyId = companyId;
                    newItem.IsPublished = true;
                    newItem.HasDeleted = false;
                    newItem.Orders = item.Orders;

                    var category = new Category();
                    category.Image = item.Category.Image;
                    category.ParentId = item.Category.ParentId == null ? (int?)null : mapIds[parentId ?? 0];
                    category.TypeId = item.Category.TypeId;
                    newItem.Category = category;

                    foreach (var cl in item.Category.CategoryLanguages)
                    {
                        var catLang = new CategoryLanguage();
                        catLang.Description = cl.Description;
                        catLang.Title = cl.Title;
                        catLang.LanguageId = cl.LanguageId;
                        newItem.Category.CategoryLanguages.Add(catLang);
                    }

                    this._itemDAL.Add(newItem);
                    this.DatabaseFactory.Get().SaveChanges();
                    mapIds[item.Id] = newItem.Id;

                    this.CopyCategories(mapIds, categories, item.Id, userName, companyId);
                }
            }
        }        

        private string VirtualPath(string folderPath, string fileName)
        {
            return string.IsNullOrEmpty(fileName) ? string.Empty : string.Format("{0}{1}{2}", this._domainStore, folderPath, fileName);
        }

        private void CopyFolder(string source, string destination)
        {
            try
            {
                var th = new System.Threading.Thread(delegate()
                {
                    // copy folder upload tren host public
                    string spath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory.Replace("\\", "/"), source);
                    string dpath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory.Replace("\\", "/"), destination);
                    IOStream.CopyFolder(spath, dpath, true);

                    // copy folder upload tren may admin
                    var url = SettingsManager.AppSettings.DomainStore + SettingsManager.Constants.JsonPage + "/CopyFolder";

                    var json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(new
                    {
                        Source = source,
                        Destination = destination,
                        Token = Encrypt.EnCode(source + destination)
                    });

                    (new URL()).PostJson(url, json);
                });
                th.Start();
            }
            catch (Exception ex)
            {
                throw new BusinessLogicLayerException(string.Format("Đăng ký không thành công do không sao chép được hình ảnh: {0}.", ex.Message));
            }
        }
        #endregion
    }
}